#ifndef __HIGMAC_MDIO_H__
#define __HIGMAC_MDIO_H__

#include "higmac.h"
#include <linux/kernel.h>
#include "util.h"
#include "gmac.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */

struct mii_bus {
    void *priv;
};
/* 0:mdio operation done, 1: start mdio operation */
#define mdio_cmd          MK_BITS(20, 1)
#define mdio_wr_data      MK_BITS(0, 16)
#define mdio_rdata_status MK_BITS(0, 1)

#define mdio_cmd_read  (2)
#define mdio_cmd_write (1)

#define higmac_mdio_readl(ld, ofs) \
    ({ unsigned int reg = readl((ld)->gmac_iobase + (ofs)); \
        higmac_trace(2, "readl(0x%04X) = 0x%08lX", (ofs), reg); reg; })

#define higmac_mdio_writel(ld, v, ofs)                     \
    do {                                                   \
        writel(v, (ld)->gmac_iobase + (ofs));              \
        higmac_trace(2, "writel(0x%04X) = 0x%08lX", (ofs), \
           (unsigned int)(v));                            \
    } while (0)

#define higmac_mdio_writel_bits(ld, v, ofs, bits_desc)                                                                 \
    do {                                                                                                               \
        unsigned int _bits_desc = bits_desc;                                                                           \
        unsigned int _shift = (_bits_desc) >> 16;                                                                      \
        unsigned int _reg = higmac_mdio_readl(ld, ofs);                                                                \
        unsigned int _mask = ((_bits_desc & 0x3F) < 32) ? (((1 << (_bits_desc & 0x3F)) - 1) << (_shift)) : 0xffffffff; \
        higmac_mdio_writel(ld, (_reg & (~_mask))\
                | (((v) << (_shift)) & _mask), ofs);                                                          \
    } while (0)

#define higmac_mdio_readl_bits(ld, ofs, bits_desc) \
    ({    unsigned int _bits_desc = bits_desc; \
        unsigned int _shift = (_bits_desc) >> 16; \
        unsigned int _mask = ((_bits_desc & 0x3F) < 32) ? \
         (((1 << (_bits_desc & 0x3F)) - 1) << (_shift)) : 0xffffffff; \
        (higmac_mdio_readl(ld, ofs) & _mask) >> (_shift); })

#define mdio_mk_rwctl(rw, phy_exaddr, phy_regnum) \
    (((0x1) << 20) |  \
        (((rw)&0x3) << 16) |  \
        (((phy_exaddr)&0x1f) << 8) |  \
        ((phy_regnum)&0x1f))

#define mdio_start_phyread(ld, phy_addr, regnum)        \
    higmac_mdio_writel(ld,                              \
        mdio_mk_rwctl(mdio_cmd_read, phy_addr, regnum), \
        REG_MDIO_SINGLE_CMD)

#define mdio_get_phyread_val(ld) \
    (higmac_mdio_readl(ld, REG_MDIO_SINGLE_DATA) >> 16)

#define mdio_set_phywrite_val(ld, val)                                        \
    do {                                                                      \
        int reg = 0;                                                          \
        higmac_mdio_writel_bits(ld, val, REG_MDIO_SINGLE_DATA, mdio_wr_data); \
        reg = higmac_mdio_readl(ld, REG_MDIO_SINGLE_DATA);                    \
        higmac_trace(2, "write reg 0x%x, bits:0x%x= 0x%x, then read = 0x%x",  \
            REG_MDIO_SINGLE_DATA, mdio_wr_data, val, reg);                    \
    } while (0)

#define mdio_phywrite(ld, phy_addr, regnum)              \
    higmac_mdio_writel(ld,                               \
        mdio_mk_rwctl(mdio_cmd_write, phy_addr, regnum), \
        REG_MDIO_SINGLE_CMD)

#define test_mdio_ready(ld) \
    (higmac_mdio_readl_bits((ld), REG_MDIO_SINGLE_CMD, mdio_cmd) == 0)

#define test_mdio_read_data_done(ld) \
    (higmac_mdio_readl_bits(ld, REG_MDIO_RDATA_STATUS, mdio_rdata_status)\
         == 0)

int higmac_mdiobus_driver_init(struct higmac_netdev_local *ld, char *bus_id);

void higmac_mdiobus_driver_exit(struct higmac_netdev_local *ld);

// int higmac_mdio_read(struct mii_bus *bus, int phy, int reg);
int higmac_mdio_read(struct higmac_netdev_local *netdev, int phy, int reg);

int wait_mdio_ready(struct higmac_netdev_local *ld);

int higmac_mdio_write(struct mii_bus *bus, int phy, int reg, unsigned short val);

#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif
